package com.probox.modules.controller.wechat;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.probox.common.core.enums.DataStatus;
import com.probox.common.core.enums.WechatAuditStatusEnum;
import com.probox.common.core.enums.WechatAuthorizationStatusEnum;
import com.probox.common.core.utils.SecurityUtils;
import com.probox.common.core.web.controller.BaseController;
import com.probox.common.core.web.domain.AjaxResult;
import com.probox.common.entity.domain.wechat.WechatAuthorizationInfo;
import com.probox.common.entity.mapper.wechat.WechatAuthorizationInfoMapper;
import com.probox.common.entity.service.wechat.WechatMiniService;
import com.probox.common.entity.service.wechat.WechatPlatformService;
import com.probox.common.log.annotation.Log;
import com.probox.common.log.enums.BusinessType;
import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * 供应商地址Controller
 *
 * @author usoft
 * @date 2021-05-28
 */
@RestController
@RequestMapping("/wechat/weixin")
public class WechatController extends BaseController {

    @Autowired
    private WechatPlatformService wechatPlatformService;

    @Autowired
    private WechatMiniService wechatMiniService;

    //授权信息对象Mapper
    @Autowired
    private WechatAuthorizationInfoMapper authorizationInfoMapper;



    /**
     * 微信第三方平台后台事件推送
     */
    @RequestMapping(method = {RequestMethod.GET,RequestMethod.POST},value = "/event")
    public void event(HttpServletRequest request, HttpServletResponse response,
                        @RequestBody String xml,
                        @RequestParam(value = "signature", required = false) String signature,
                        @RequestParam(value = "timestamp", required = false) String timeStamp,
                        @RequestParam(value = "nonce", required = false) String nonce,
                        @RequestParam(value = "encrypt_type", required = false) String encryptType,
                        @RequestParam(value = "msg_signature", required = false) String msgSignature) throws IOException, AesException, ParserConfigurationException, SAXException {
        logger.info("收到微信后端ticket事件推送....");
        logger.info("收到的加密xml内容="+xml);
        WXBizMsgCrypt pc = new WXBizMsgCrypt(WechatPlatformService.token, WechatPlatformService.key, WechatPlatformService.appId);
        String decryptMsg = pc.decryptMsg(msgSignature, timeStamp, nonce, xml);
        logger.info("解密xml内容="+decryptMsg);
        response.getWriter().write("success");
        response.getWriter().flush();
        response.getWriter().close();

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
        dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        dbf.setXIncludeAware(false);
        dbf.setExpandEntityReferences(false);
        DocumentBuilder db = dbf.newDocumentBuilder();
        StringReader sr = new StringReader(decryptMsg);
        InputSource is = new InputSource(sr);
        Document document = db.parse(is);
        Element root = document.getDocumentElement();
        NodeList nodeList  = root.getElementsByTagName("ComponentVerifyTicket");
        if (nodeList.getLength()>0){
            wechatPlatformService.setTicket(nodeList.item(0).getTextContent());
            wechatPlatformService.getApiComponentToken();
        }
    }


    /**
     * 开始PC授权
     */
    @ApiOperation(value="开始PC端授权")
    @Log(title = "开始PC端授权", businessType = BusinessType.GRANT)
    @RequestMapping(method = {RequestMethod.GET,RequestMethod.POST},value = "/grant")
    public AjaxResult grantByPc(@RequestBody String authorizationJson) throws IOException {
        JSONObject jsonObject = JSONObject.parseObject(authorizationJson);
        String indexUrl =  jsonObject.getString("indexUrl");
        String authorizationType =  jsonObject.getString("authorizationType");
        String authorizationStatus =  jsonObject.getString("authorizationStatus");
        logger.info("接收到授权状态:"+authorizationStatus);
        if(StringUtils.isEmpty(authorizationStatus)){
            authorizationStatus = "0";
        }
        logger.info("授权前请求路径=>"+indexUrl);
        logger.info("授权前类型=>"+authorizationType);
        logger.info("授权状态=>"+authorizationStatus);
        logger.info("开始PC端授权....");
        logger.info("grant to 用户id=>"+SecurityUtils.getUserId());
        logger.info("grant to 平台id=>"+SecurityUtils.getPlatformId());
        String redirect_uri = URLEncoder.encode("https://console.yooruan.com/api/prod-api/wechat/weixin/grantCallBack?platformid="+SecurityUtils.getPlatformId()+"&url="+indexUrl+"&authorizationType="+authorizationType+"&authorizationStatus="+authorizationStatus,"utf-8");
        String url = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid="+WechatPlatformService.appId+"&pre_auth_code="+wechatPlatformService.getApiCreatePreauthcode()+"&redirect_uri="+redirect_uri+"&auth_type=3";
        Map<String,String> map = new HashMap<>();
        map.put("grantUrl",url);
        return AjaxResult.success(map);
    }

    /**
     * 开始移动端授权
     * @param response
     * @return
     * @throws IOException
     */
    @RequestMapping(method = {RequestMethod.GET,RequestMethod.POST},value = "/grantMobile")
    public AjaxResult grantToMobile(HttpServletResponse response) throws IOException {
        logger.info("开始移动端授权....");
        String redirect_uri = URLEncoder.encode("https://console.yooruan.com/api/prod-api/wechat/weixin/grantCallBack","utf-8");
        String url = "https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&no_scan=1&component_appid="+WechatPlatformService.appId+"&pre_auth_code="+wechatPlatformService.getApiCreatePreauthcode()+"&redirect_uri="+redirect_uri+"&auth_type=3#wechat_redirect";
        Map<String,String> map = new HashMap<>();
        map.put("grantUrl",url);
        return AjaxResult.success(map);
    }


    /**
     * 授权回调
     * auth_code=xxx&expires_in=600
     */
    @RequestMapping(method = {RequestMethod.GET,RequestMethod.POST},value = "/grantCallBack")
    public void grantCallBack(HttpServletRequest request,HttpServletResponse resp) throws IOException {
        logger.info("授权回调....");
        String platformid = request.getParameter("platformid");
        if (platformid != null){
            logger.info("当前平台id=>"+platformid);
        }
        String authorizationType = request.getParameter("authorizationType");
        String authorizationStatus = request.getParameter("authorizationStatus");
        String auth_code = request.getParameter("auth_code");
        String expires_in = request.getParameter("expires_in");
        logger.info("授权成功后的 auth_code="+auth_code);
        logger.info("授权成功后的 expires_in="+expires_in);
        logger.info("授权成功后的 authorizationType="+authorizationType);
        logger.info("授权成功后的 authorizationStatus="+authorizationStatus);
        String url = request.getParameter("url");
        String apiQueryAuth = wechatPlatformService.getApi_query_auth(auth_code, expires_in, platformid, authorizationType,authorizationStatus,null,null);
        logger.info("绑定授权信息返回"+apiQueryAuth);
        String success = null;
        String error = null;
        if ("success".equals(apiQueryAuth)){
            HashMap authorizerInfo = wechatMiniService.getAuthorizerInfo(authorizationType,platformid);
            if (authorizerInfo!=null){
                Object authorizerStatus = authorizerInfo.get("authorizerStatus");
                if ("0".equals(String.valueOf(authorizerStatus))){
                    Object miniProgramInfo = authorizerInfo.get("MiniProgramInfo");
                    String authorizerInfoStatus = null;
                    if (miniProgramInfo!=null){
                        //小程序
                        authorizerInfoStatus = "小程序";
                    }else {
                        //公众号
                        authorizerInfoStatus = "公众号";
                    }
                    success = authorizerInfoStatus+"授权成功";
                }
            }
            //添加服务器域名
            String action = "";
            if(WechatAuthorizationStatusEnum.againAuthorization.getKey().equals(authorizationStatus)){
                action = "set";
            }else {
                action = "add";
            }
            wechatMiniService.modifyDomain(action,platformid,authorizationType);
            //添加业务域名
            wechatMiniService.setwebviewdomain(action,platformid,authorizationType);
        }else if(apiQueryAuth.contains("authorization_info")){
            logger.info("注册小程序返回");
            success = apiQueryAuth;
        }else {
            error = apiQueryAuth;
        }
        //跳转回授权页
        if (!StringUtils.isEmpty(success)){
            if(url.contains("/register/data")){
                resp.sendRedirect(url+"?success="+ URLEncoder.encode(success.toString(),"UTF-8"));
            }else{
                resp.sendRedirect(url);
            }
        }else {
            resp.sendRedirect(url+"?error="+ URLEncoder.encode(error.toString(),"UTF-8"));
        }

    }

    /**
     * 解析输入流
     * @param inStream
     * @return
     * @throws Exception
     */
    public static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        boolean var3 = false;
        int len;
        while((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        return outSteam.toByteArray();
    }

    @RequestMapping(method = {RequestMethod.GET,RequestMethod.POST},value = "/{appId}/result")
    public  void ecryptMsg(HttpServletRequest request, @PathVariable("appId") String appid,
                             HttpServletResponse response) throws Exception {

        logger.info(appid+":收到微信后端消息推送....");
        String timestamp=request.getParameter("timestamp");
        String nonce=request.getParameter("nonce");
        String msg_signature=request.getParameter("msg_signature");

        String postData = new String(readInputStream(request.getInputStream()), "UTF-8");
        logger.info("收到的消息内容===>"+postData);

        WXBizMsgCrypt pc = new WXBizMsgCrypt(WechatPlatformService.token, WechatPlatformService.key, WechatPlatformService.appId);
        String ToUserName = "";
        String FromUserName = "";
        String MsgType = "";
        String Content = "";
        String Event = "";
        String Reason = "";
        String xml = pc.decryptMsg(msg_signature,timestamp,nonce,postData);
        logger.info("收到的解密消息内容===>"+xml);
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
            dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            dbf.setXIncludeAware(false);
            dbf.setExpandEntityReferences(false);
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(xml);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);

            Element root = document.getDocumentElement();
            ToUserName = root.getElementsByTagName("ToUserName").item(0).getTextContent();
            FromUserName = root.getElementsByTagName("FromUserName").item(0).getTextContent();
            MsgType = root.getElementsByTagName("MsgType").item(0).getTextContent();
            if (!StringUtils.isEmpty(root.getElementsByTagName("Content")) && root.getElementsByTagName("Content").getLength()>0){
                Content = root.getElementsByTagName("Content").item(0).getTextContent().trim();
            }
            Event = root.getElementsByTagName("Event").item(0).getTextContent();
            if (!StringUtils.isEmpty(root.getElementsByTagName("Reason")) && root.getElementsByTagName("Reason").getLength()>0){
                Reason = root.getElementsByTagName("Reason").item(0).getTextContent();
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("=====xml解密失败"+e);
        }
        logger.info("解密内容结果ToUserName:{},FromUserName:{},MsgType:{},Content:{},Event:{},Reason:{}",ToUserName,FromUserName,MsgType,Content,Event,Reason);
        String replyMsg = "";

        if (Content.equals("TESTCOMPONENT_MSG_TYPE_TEXT")){
            replyMsg = "<xml><ToUserName><![CDATA["+FromUserName+"]]></ToUserName><FromUserName><![CDATA["+ToUserName+"]]></FromUserName><CreateTime>"+timestamp
                    +"</CreateTime><MsgType><![CDATA["+MsgType+"]]></MsgType><Content>TESTCOMPONENT_MSG_TYPE_TEXT_callback</Content><Event><![CDATA["+Event+"]]></Event><Reason><![CDATA["+Reason+"]]></Reason></xml>";
            logger.info("文本返回消息: " + replyMsg);
            String mingwen = pc.encryptMsg(replyMsg, timestamp, nonce);
            logger.info("加密后文本返回值: " + mingwen);
            response.getWriter().write(mingwen);
            response.getWriter().flush();
            response.getWriter().close();
        }else if (Content.indexOf("QUERY_AUTH_CODE")>=0){
            logger.info("发送消息给: " + FromUserName);
            response.getWriter().write("");
            response.getWriter().flush();
            response.getWriter().close();
            String queryAuthCode = Content.replaceAll("QUERY_AUTH_CODE:","");
            wechatPlatformService.sendMessage(FromUserName,queryAuthCode);
        }
        //处理审核结果
        if ("weapp_audit_success".equals(Event)){
            WechatAuthorizationInfo wechatAuthorizationInfo = authorizationInfoMapper.selectById(appid);
            if (!DataStatus.delete.getKey().equals(wechatAuthorizationInfo.getDataStatus())){
                logger.info("===当前"+appid+"==自动发布状态:"+wechatAuthorizationInfo.getIssueStatus()+"审核状态"+Event);
                if ("true".equals(wechatAuthorizationInfo.getIssueStatus())){
                    logger.info("开始自动发布,修改发布状态");
                    wechatMiniService.release(appid,wechatAuthorizationInfo.getAuthorizerType());
                }else{
                    logger.info("开始修改发布状态");
                    wechatAuthorizationInfo.setAuditStatus(WechatAuditStatusEnum.reviewSuccess.getKey());
                    wechatAuthorizationInfo.setUpdateTime(DateUtil.date());
                    authorizationInfoMapper.updateById(wechatAuthorizationInfo);
                }
            }
        }

    }

}
